/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "imageknife_request.h"
#include "imageknife_loader_internal.h"
#include "imageknife_request_internal.h"
#include "imageknife_data_napi.h"
#include "log.h"

namespace ImageKnifePro {
void ImageKnifeLoaderInternal::AddMemoryCacheInterceptor(std::shared_ptr<MemoryCacheInterceptor> interceptor,
                                                         ImageKnifeLoader::Position position)
{
    IMAGE_KNIFE_CHECK(interceptor == nullptr, "Null memory interceptor");

    // 责任链为空
    if (memoryInterceptorHead_ == nullptr) {
        memoryInterceptorHead_ = memoryInterceptorTail_ = interceptor;
        return;
    }

    if (position == ImageKnifeLoader::Position::START) {
        interceptor->SetNext(memoryInterceptorHead_);
        memoryInterceptorHead_ = interceptor;
    } else {
        memoryInterceptorTail_->SetNext(interceptor);
        memoryInterceptorTail_ = interceptor;
    }
}

void ImageKnifeLoaderInternal::AddFileCacheInterceptor(std::shared_ptr<FileCacheInterceptor> interceptor,
                                                       ImageKnifeLoader::Position position)
{
    IMAGE_KNIFE_CHECK(interceptor == nullptr, "Null file interceptor");

    // 责任链为空
    if (fileInterceptorHead_ == nullptr) {
        fileInterceptorHead_ = fileInterceptorTail_ = interceptor;
        return;
    }

    if (position == ImageKnifeLoader::Position::START) {
        interceptor->SetNext(fileInterceptorHead_);
        fileInterceptorHead_ = interceptor;
    } else {
        fileInterceptorTail_->SetNext(interceptor);
        fileInterceptorTail_ = interceptor;
    }
}

void ImageKnifeLoaderInternal::AddLoadInterceptor(std::shared_ptr<LoadInterceptor> interceptor,
                                                  ImageKnifeLoader::Position position)
{
    IMAGE_KNIFE_CHECK(interceptor == nullptr, "Null load interceptor");

    // 责任链为空
    if (loadInterceptorHead_ == nullptr) {
        loadInterceptorHead_ = loadInterceptorTail_ = interceptor;
        return;
    }

    if (position == ImageKnifeLoader::Position::START) {
        interceptor->SetNext(loadInterceptorHead_);
        loadInterceptorHead_ = interceptor;
    } else {
        loadInterceptorTail_->SetNext(interceptor);
        loadInterceptorTail_ = interceptor;
    }
}

void ImageKnifeLoaderInternal::AddDecodeInterceptor(std::shared_ptr<DecodeInterceptor> interceptor,
                                                    ImageKnifeLoader::Position position)
{
    IMAGE_KNIFE_CHECK(interceptor == nullptr, "Null decode interceptor");

    // 责任链为空
    if (decodeInterceptorHead_ == nullptr) {
        decodeInterceptorHead_ = decodeInterceptorTail_ = interceptor;
        return;
    }

    if (position == ImageKnifeLoader::Position::START) {
        interceptor->SetNext(decodeInterceptorHead_);
        decodeInterceptorHead_ = interceptor;
    } else {
        decodeInterceptorTail_->SetNext(interceptor);
        decodeInterceptorTail_ = interceptor;
    }
}

bool ImageKnifeLoaderInternal::LoadFromMemory(std::shared_ptr<ImageKnifeTaskInternal> task)
{
    IMAGE_KNIFE_CHECK_RETURN(task == nullptr, false);
    auto request = task->GetImageKnifeRequestInternal();
    IMAGE_KNIFE_CHECK_RETURN(request == nullptr, false);

    ImageRequestType type = task->GetImageRequestType();
    if (type == ImageRequestType::MAIN_SRC) {
        request->GetImageInfo().errorInfo.phase = ImageKnifeDataNapi::phaseToStringMap[LoadPhase::MEMORY_CACHE];
        request->GetImageInfo().timeInfo.memoryCheckStartTime = ImageKnifeDataNapi::GetTimestamp();
    }

    if (memoryInterceptorHead_ == nullptr) {
        task->FatalError("Not Find Memory Interceptor!");
        return false;
    }

    try {
        task->cacheTask.type = CacheTaskType::READ;
        task->cacheTask.cacheKey = task->memoryKey;
        bool result = memoryInterceptorHead_->Process(task);
        if (type == ImageRequestType::MAIN_SRC) {
            request->GetImageInfo().timeInfo.memoryCheckEndTime = ImageKnifeDataNapi::GetTimestamp();
            if (!result) {
                return false;
            }

            request->GetImageInfo().imageSourceFrom = ImageSourceFrom::FROM_MEMORY_CACHE;
            auto option = request->GetImageKnifeOption();
            IMAGE_KNIFE_CHECK_RETURN(option == nullptr, false);
            if (option->onLoadListener != nullptr && option->onLoadListener->onMemorySuccess != nullptr) {
                option->onLoadListener->onMemorySuccess(request->GetImageInfo());
            }
        }
        return result;
    } catch (std::exception err) {
        task->FatalError("MemoryInterceptor: " + std::string(err.what()));
        return false;
    }
}

bool ImageKnifeLoaderInternal::LoadFromFile(std::shared_ptr<ImageKnifeTaskInternal> task)
{
    IMAGE_KNIFE_CHECK_RETURN(task == nullptr, false);
    auto request = task->GetImageKnifeRequestInternal();
    IMAGE_KNIFE_CHECK_RETURN(request == nullptr, false);

    ImageRequestType type = task->GetImageRequestType();
    if (type == ImageRequestType::MAIN_SRC) {
        request->GetImageInfo().errorInfo.phase = ImageKnifeDataNapi::phaseToStringMap[LoadPhase::FILE_CACHE];
        request->GetImageInfo().timeInfo.diskCheckStartTime = ImageKnifeDataNapi::GetTimestamp();
    }

    if (fileInterceptorHead_ == nullptr) {
        task->FatalError("Not Find File Interceptor!");
        return false;
    }

    try {
        task->cacheTask.type = CacheTaskType::READ;
        task->cacheTask.cacheKey = task->fileKey;
        bool result = fileInterceptorHead_->Process(task);
        if (type == ImageRequestType::MAIN_SRC) {
            request->GetImageInfo().timeInfo.diskCheckEndTime = ImageKnifeDataNapi::GetTimestamp();
            if (result) {
                request->GetImageInfo().imageSourceFrom = ImageSourceFrom::FROM_FILE_CACHE;
            }
        }
        return result;
    } catch (std::exception err) {
        task->FatalError("FileInterceptor: " + std::string(err.what()));
    }

    return false;
}


bool ImageKnifeLoaderInternal::WriteCacheToMemory(std::shared_ptr<ImageKnifeTaskInternal> task)
{
    IMAGE_KNIFE_CHECK_RETURN(task == nullptr, false);
    if (memoryInterceptorHead_ == nullptr) {
        task->FatalError("Not Find Memory Interceptor!");
        return false;
    }

    try {
        task->cacheTask.type = CacheTaskType::WRITE;
        task->cacheTask.cacheKey = task->memoryKey;
        return memoryInterceptorHead_->Process(task);
    } catch (std::exception err) {
        task->FatalError("MemoryInterceptor: " + std::string(err.what()));
        return false;
    }
}

bool ImageKnifeLoaderInternal::WriteCacheToFile(std::shared_ptr<ImageKnifeTaskInternal> task)
{
    IMAGE_KNIFE_CHECK_RETURN(task == nullptr, false);
    if (fileInterceptorHead_ == nullptr) {
        task->FatalError("Not Find File Interceptor!");
        return false;
    }

    try {
        task->cacheTask.type = CacheTaskType::WRITE;
        task->cacheTask.cacheKey = task->fileKey;
        return fileInterceptorHead_->Process(task);
    } catch (std::exception err) {
        task->FatalError("FileInterceptor: " + std::string(err.what()));
        return false;
    }
}

void ImageKnifeLoaderInternal::RecordLoadingInfo(const bool &result, const ImageRequestType &type, const int &httpCode,
                                                 std::shared_ptr<ImageKnifeRequestInternal> &request)
{
    if (type == ImageRequestType::MAIN_SRC) {
        request->GetImageInfo().timeInfo.loadImageEndTime = ImageKnifeDataNapi::GetTimestamp();
        if (result) {
            request->GetImageInfo().imageSourceFrom = httpCode == 0 ?
                ImageSourceFrom::FROM_LOCAL_FILE : ImageSourceFrom::FROM_NETWORK_DOWNLOAD;
        } else {
            request->GetImageInfo().errorInfo.code = httpCode == 0 ?
                LoadFailedCode::LOAD_LOCAL_FILE_FAILED : LoadFailedCode::LOAD_IMAGE_FAILED;
        }

        auto option = request->GetImageKnifeOption();
        IMAGE_KNIFE_CHECK(option == nullptr);
        if (request->GetImageInfo().imageSourceFrom == ImageSourceFrom::FROM_NETWORK_DOWNLOAD &&
            option->onLoadListener != nullptr && option->onLoadListener->onDownloadSuccess != nullptr) {
            option->onLoadListener->onDownloadSuccess(request->GetImageInfo());
        }
    }
}

bool ImageKnifeLoaderInternal::DownloadImage(std::shared_ptr<ImageKnifeTaskInternal> task)
{
    IMAGE_KNIFE_CHECK_RETURN(task == nullptr, false);
    auto request = task->GetImageKnifeRequestInternal();
    IMAGE_KNIFE_CHECK_RETURN(request == nullptr, false);

    ImageRequestType type = task->GetImageRequestType();
    if (type == ImageRequestType::MAIN_SRC) {
        request->GetImageInfo().errorInfo.phase = ImageKnifeDataNapi::phaseToStringMap[LoadPhase::LOAD_IMAGE_FETCH];
        request->GetImageInfo().timeInfo.loadImageStartTime = ImageKnifeDataNapi::GetTimestamp();
    }

    if (loadInterceptorHead_ == nullptr) {
        task->FatalError("Not Find Load Interceptor!");
        return false;
    }

    task->cacheTask.type = CacheTaskType::NONE;
    try {
        bool result = loadInterceptorHead_->Process(task);
        if (task->IsDetached()) {
            return true;
        }
        RecordLoadingInfo(result, type, task->httpCode, request);
        return result;
    } catch (std::exception err) {
        task->FatalError("DownloadInterceptor: " + std::string(err.what()));
        return false;
    }
}

bool ImageKnifeLoaderInternal::DecodeImage(std::shared_ptr<ImageKnifeTaskInternal> task)
{
    IMAGE_KNIFE_CHECK_RETURN(task == nullptr, false);
    auto request = task->GetImageKnifeRequestInternal();
    IMAGE_KNIFE_CHECK_RETURN(request == nullptr, false);

    ImageRequestType type = task->GetImageRequestType();
    if (type == ImageRequestType::MAIN_SRC) {
        request->GetImageInfo().errorInfo.phase = ImageKnifeDataNapi::phaseToStringMap[LoadPhase::DECODE];
        request->GetImageInfo().timeInfo.decodeStartTime = ImageKnifeDataNapi::GetTimestamp();
    }

    if (decodeInterceptorHead_ == nullptr) {
        task->FatalError("Not Find Decode Interceptor!");
        return false;
    }

    task->cacheTask.type = CacheTaskType::NONE;
    try {
        bool result = decodeInterceptorHead_->Process(task);
        if (type == ImageRequestType::MAIN_SRC) {
            request->GetImageInfo().timeInfo.decodeEndTime = ImageKnifeDataNapi::GetTimestamp();

            auto option = request->GetImageKnifeOption();
            IMAGE_KNIFE_CHECK_RETURN(option == nullptr, false);
            if (result && option->onLoadListener != nullptr &&
                option->onLoadListener->onDecodeSuccess != nullptr) {
                option->onLoadListener->onDecodeSuccess(request->GetImageInfo());
            }
        }
        return result;
    } catch (std::exception err) {
        task->FatalError("DecodeInterceptor: " + std::string(err.what()));
        return false;
    }
}

} // end of namespace
